/**
 * File: FileType.java
 * Created by: mhaimel
 * Created on: Apr 16, 2010
 * CVS:  $Id: FileType.java 1.0 Apr 16, 2010 3:55:23 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.utils.data;

import java.io.IOException;
import java.io.PrintWriter;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;

import uk.ac.ebi.curtain.utils.file.EntryMapper;
import uk.ac.ebi.curtain.utils.file.EntryMapperBean;
import uk.ac.ebi.curtain.utils.file.FileInfo;
import uk.ac.ebi.curtain.utils.file.ParseUtil;
import uk.ac.ebi.curtain.utils.file.SplitUtil;
import uk.ac.ebi.curtain.utils.file.WriteUtil;
import uk.ac.ebi.curtain.utils.file.fasta.FastaParser;
import uk.ac.ebi.curtain.utils.file.fasta.FastaWriteUtil;
import uk.ac.ebi.curtain.utils.file.impl.FastqParser;
import uk.ac.ebi.curtain.utils.file.impl.FastqSplitUtil;
import uk.ac.ebi.curtain.utils.file.impl.FastqWriterUtil;
import uk.ac.ebi.curtain.utils.file.maq.MaqMapParser;
import uk.ac.ebi.curtain.utils.io.impl.FileIO;
import uk.ac.ebi.curtain.utils.match.MatchParser;
import uk.ac.ebi.curtain.utils.match.sam.SamMatchParser;
import uk.ac.ebi.velvet.model.Strand;

/**
 * @author mhaimel
 *
 */
public enum FileType {
	fasta(new FastaParser(), new FastaWriteUtil(),null,new String[]{".fa",".fasta"}, null),
	fastq(new FastqParser(), new FastqWriterUtil(),new FastqSplitUtil(),new String[]{".fastq","fq"}, null),
	maq(null,null,null,new String[]{".map"}, new MaqMapParser()),
	sam(null,null,null,new String[]{".sam"}, new SamMatchParser()),
	bam(null,null,null,new String[]{".bam"}, new SamMatchParser())
	;
	
	private final ParseUtil parser;
	private final WriteUtil writer;
	private final String[] postfix;
	private final SplitUtil splitter;
	private final MatchParser matchparser;
	
	private FileType(ParseUtil parser, WriteUtil writer, SplitUtil split, String[] postfix, MatchParser matchparser) {
		this.parser = parser;
		this.writer = writer;
		this.postfix = postfix;
		this.splitter = split;
		this.matchparser = matchparser;
	}
	
	public static FileType[] getMapTypes(){
		return new FileType[]{maq,sam,bam};
	}
	
	public static FileType[] getSequenceTypes(){
		return new FileType[]{fasta,fastq};
	}
	
	public static FileType getFromPostfix(String name){
		for(FileType ft : FileType.values()){
			for(String pf : ft.postfix){
				if(name.endsWith(pf)){
					return ft;
				}
			}
		}
		return null;
	}
	
	public boolean isIn(FileType ... typeArr){
		for(FileType type : typeArr){
			if(this.equals(type)){
				return true;
			}
		}
		return false;
	}
	
	public String getPostfix() {
		return postfix[0];
	}
	public String[] getPostfixes() {
		return postfix;
	}
	public String addPostfix(String name){
		return name+getPostfix();
	}
	public String removePostfix(String name){
		for(String pf : getPostfixes()){
			name = name.replace(pf, StringUtils.EMPTY);
		}
		return name;
	}
	
	public SplitUtil getSplitter(){
		if(null == splitter){
			throw new NotImplementedException(
					"Type not supported for splitting: " + this);
		}
		return splitter;
	}
	
	public MatchParser getMatchParser(){
		if(null == matchparser){
			throw new NotImplementedException(
					"Type not supported for parsing: " + this);
		}
		return matchparser;
	}
	
	public ParseUtil getParser(){
		if(null == parser){
			throw new NotImplementedException(
					"Type not supported for parsing: " + this);
		}
		return parser;
	}
	
	public WriteUtil getWriter() {
		if(null == writer){
			throw new NotImplementedException(
					"Type not supported for writing: " + this);
		}
		return writer;
	}
	
	
	public static void transform(FileInfo from, FileInfo to) throws IOException{
		PrintWriter out = null;
		try{
			out = new FileIO(to.getFile()).getPrintWriter();
			from.getFileType().getParser().parse(
					from, 
					new UniversalEntryMapper(to.getFileType().getWriter(),out));
		} finally{
			IOUtils.closeQuietly(out);
		}
	}
	
	public static class UniversalEntryMapper implements EntryMapper{
		
		private final WriteUtil writer;
		private final PrintWriter out;
		private volatile IdentifierProcessor from = null;
		private volatile IdentifierProcessor to = null;

		public UniversalEntryMapper(WriteUtil writer,PrintWriter out) {
			this.writer = writer;
			this.out = out;
		}

		@Override
		public void mapEntry(EntryMapperBean bean) {
			writer.write(getOutWriter(), process(bean));
		}
		
		private EntryMapperBean process(EntryMapperBean bean){
			EntryMapperBean newBean = bean;
			if(null != from && null != to){
				newBean = new EntryMapperBean(bean);
				Strand strand = from.getStrand(newBean.getId());
				newBean.setId(
						to.addStrandInformation(
						from.removeStrandInformation(newBean.getId()), strand));
			}
			return newBean;
		}
		
		public void setIdProcessorFrom(IdentifierProcessor from) {
			this.from = from;
		}
		public void setIdProcessorTo(IdentifierProcessor to) {
			this.to = to;
		}
		public PrintWriter getOutWriter() {
			return out;
		}			
		
	}
	
}
